package com.dsteiner.xelavos.client.controller;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;

import com.dsteiner.widgets.client.JsUtil;
import com.dsteiner.widgets.client.StringUtil;
import com.dsteiner.widgets.client.domain.DataRequest;
import com.dsteiner.widgets.client.domain.ModelObject;
import com.dsteiner.widgets.client.event.DataUpdateEvent;
import com.dsteiner.widgets.client.event.EventListener;
import com.dsteiner.widgets.client.event.EventManager;
import com.dsteiner.widgets.client.event.EventManagerImpl;
import com.dsteiner.widgets.client.event.MessageEvent;
import com.dsteiner.widgets.client.rpc.CrudResult;
import com.dsteiner.widgets.client.rpc.SearchRequest;
import com.dsteiner.widgets.client.rpc.SearchResult;
import com.dsteiner.xelavos.client.constants.Constants;
import com.dsteiner.xelavos.client.domain.DataResult;
import com.dsteiner.xelavos.client.domain.Job;
import com.dsteiner.xelavos.client.domain.Leisure;
import com.dsteiner.xelavos.client.domain.Product;
import com.dsteiner.xelavos.client.domain.SavedSearch;
import com.dsteiner.xelavos.client.domain.SearchRequestXelavos;
import com.dsteiner.xelavos.client.domain.Service;
import com.dsteiner.xelavos.client.domain.TrackComments;
import com.dsteiner.xelavos.client.domain.User;
import com.dsteiner.xelavos.client.domain.UserStats;
import com.dsteiner.xelavos.client.event.DeleteObjectEvent;
import com.dsteiner.xelavos.client.event.GetObjectEvent;
import com.dsteiner.xelavos.client.event.SavedSearchEvent;
import com.dsteiner.xelavos.client.event.SearchEvent;
import com.dsteiner.xelavos.client.event.SearchInfoEvent;
import com.dsteiner.xelavos.client.event.SearchResultEvent;
import com.dsteiner.xelavos.client.event.TrackCommentsEvent;
import com.dsteiner.xelavos.client.model.AuthenticationModel;
import com.dsteiner.xelavos.client.model.DataModel;
import com.dsteiner.xelavos.client.model.MainTabModel;
import com.dsteiner.xelavos.client.model.MyEntriesModel;
import com.dsteiner.xelavos.client.model.SearchModel;
import com.dsteiner.xelavos.client.model.TopNewModel;
import com.dsteiner.xelavos.client.model.UserStatsModel;
import com.dsteiner.xelavos.client.model.MainTabModel.Tab;
import com.dsteiner.xelavos.client.rpc.DataService;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;

public class DataController {

	private TopNewModel topNewModel;
	private DataModel dataModel;
	
	private Timer updateTopNewDataTimer = new Timer(){
		@Override
		public void run() {
			Tab type = mainTabModel.getSelectedTab();
			if(type == null)
				return;
			DataRequest datarequestTopNew = DataModel.DATAREQUEST_POLL;
			datarequestTopNew.setParameter("type", type.getClassName());
			datarequestTopNew.setParameterObj("user", authenticationModel.getUser());
			dataModel.addDataRequest(datarequestTopNew);
		}
	};
	
	private Timer updateDataRequestsTimer = new Timer(){
		@Override
		public void run() {
			updateDataRequests();
		}
	};
	private AuthenticationModel authenticationModel;
	private SearchModel searchModel;
	private MainTabModel mainTabModel;
	private MyEntriesModel myEntriesModel;
	private UserStatsModel userStatsModel;
	private SearchRequestXelavos lastSearchRequest;
	private EventManager eventManager;
	
	public void registerAuthenticationModel(final AuthenticationModel authenticationModel) {
		this.authenticationModel = authenticationModel;
		authenticationModel.addPropertyChangeListener("user", new PropertyChangeListener(){
			public void propertyChange(PropertyChangeEvent evt) {
				if(authenticationModel.getUser() != null)
					updateTopNewDataTimer.run();
			}
		});
	}
	
	public void registerDataModel(final DataModel dataModel) {
		this.dataModel = dataModel;
		dataModel.addPropertyChangeListener("dataRequests", new PropertyChangeListener(){
			public void propertyChange(PropertyChangeEvent evt) {
				updateDataRequestsTimer.cancel();
				updateDataRequestsTimer.schedule(200);
			}
		});
		updateDataModel();
		updateTopNewDataTimer.scheduleRepeating(Constants.POLL_DATA_MS);
	}

	public void registerEventManager(final EventManager eventManager) {
		this.eventManager = eventManager;
		eventManager.registerListener(DataUpdateEvent.class, new EventListener<DataUpdateEvent>(){
			public void onEvent(DataUpdateEvent dataUpdateEvent) {
				Object object = dataUpdateEvent.getObject();
				if((mainTabModel.isFriendsTabSelected() && object instanceof User) ||
				   (mainTabModel.isJobsTabSelected() && object instanceof Job) || 
				   (mainTabModel.isShoppingTabSelected() && object instanceof Product) ||
				   (mainTabModel.isLeisureTabSelected() && object instanceof Leisure) || 
				   (mainTabModel.isServicesTabSelected() && object instanceof Service)){
					updateTopNewDataTimer.run();
				}
			}
		});
		eventManager.registerListener(SearchEvent.class, new EventListener<SearchEvent>(){
			public void onEvent(SearchEvent searchEvent) {
				SearchRequest searchRequest = searchEvent.getSearchRequest();
				lastSearchRequest = (SearchRequestXelavos)searchRequest;
				DataRequest datarequestSearch = DataModel.DATAREQUEST_SEARCH;
				datarequestSearch.setCanceled(false);
				datarequestSearch.setParameterObj("searchRequest", (SearchRequestXelavos)searchRequest);
				dataModel.addDataRequest(datarequestSearch);
			}
		});
		eventManager.registerListener(DeleteObjectEvent.class, new EventListener<DeleteObjectEvent>(){
			public void onEvent(DeleteObjectEvent deleteObjectEvent) {
				if(!deleteObjectEvent.isConfirmed())
					return;
				final ModelObject toDeleteModelObject = deleteObjectEvent.getModelObject();
				DataService.Util.getInstance().deleteObject(toDeleteModelObject, 
						new AsyncCallback<CrudResult<ModelObject>>(){
					public void onFailure(Throwable caught) {
					}
					public void onSuccess(CrudResult result) {
						User user = (User) authenticationModel.getUser();
						user.setEntriesCount(user.getEntriesCount()-1);
						EventManagerImpl.getInstance().fireEvent(new DataUpdateEvent(user));
					}
				});
			}
		});
		eventManager.registerListener(GetObjectEvent.class, new EventListener<GetObjectEvent>(){
			public void onEvent(GetObjectEvent event) {
				String className = event.getClassName();
				if(className == null)
					className = mainTabModel.getSelectedTab().getClassName();
				DataService.Util.getInstance().getObjectById(event.getId(), 
						className , new AsyncCallback<CrudResult<ModelObject>>(){
					public void onFailure(Throwable caught) {
						caught.printStackTrace();
					}
					public void onSuccess(CrudResult result) {
						if(!StringUtil.isEmpty(result.getError())){
							JsUtil.getInstance().showAlert(result.getError());
							return;
						}
						DataModel.DATAREQUEST_SEARCH.setCanceled(true);
						List<ModelObject> results = new ArrayList<ModelObject>();
						results.add(result.getModelObject());
						eventManager.fireEvent(new MessageEvent("se encontro el registro buscado", MessageEvent.TYPE_INFO));
						searchModel.setResults(results);
						searchModel.setCount(1);
					}
				});
			}
		});
		eventManager.registerListener(SavedSearchEvent.class, new EventListener<SavedSearchEvent>() {
			public void onEvent(SavedSearchEvent event) {
				SavedSearch savedSearch = event.getSavedSearch();
				assert lastSearchRequest != null;
				savedSearch.setSearchClassName(lastSearchRequest.getClassName());
				savedSearch.setSearchSort(lastSearchRequest.getSort().toString());
				savedSearch.setSearchText(lastSearchRequest.getText());
				DataService.Util.getInstance().createModelObject(savedSearch, new AsyncCallback<CrudResult<SavedSearch>>(){
					public void onFailure(Throwable caught) {
						caught.printStackTrace();
					}
					public void onSuccess(CrudResult<SavedSearch> result) {
					}
				});
			}
		});
		eventManager.registerListener(TrackCommentsEvent.class, new EventListener<TrackCommentsEvent>() {
			public void onEvent(TrackCommentsEvent event) {
				final TrackComments trackComments = event.getTrackComments();
				if(trackComments.getId() == null){
					DataService.Util.getInstance().createModelObject(trackComments, new AsyncCallback<CrudResult<TrackComments>>(){
						public void onFailure(Throwable caught) {
							caught.printStackTrace();
						}
						public void onSuccess(CrudResult<TrackComments> result) {
							authenticationModel.addTrackComments(result.getModelObject());
						}
					});
				}
				else{
					DataService.Util.getInstance().deleteObject(trackComments, new AsyncCallback<CrudResult<TrackComments>>(){
						public void onFailure(Throwable caught) {
							caught.printStackTrace();
						}
						public void onSuccess(CrudResult<TrackComments> result) {
							authenticationModel.removeTrackComments(trackComments);
						}
					});
				}
			}
		});
		eventManager.registerListener(SearchResultEvent.class, new EventListener<SearchResultEvent>() {
			public void onEvent(SearchResultEvent event) {
				SearchResult<ModelObject> searchResults = event.getSearchResults();
				int count = searchResults.getCount();
				String message = "";
				if(count == 0)
					message = Constants.MSG_NO_RESULTS;
				else if(count == 1)
					message = "se encontro <strong>un resultado</strong>";
				else
					message = "se encontraron <strong>" + count + " resultados</strong>";
				eventManager.fireEvent(new MessageEvent(message, MessageEvent.TYPE_INFO));
				searchModel.setResults(searchResults.getObjects());
				searchModel.setCount(count);
			}
		});

	}

	public void registerMainTabModel(MainTabModel mainTabModel) {
		this.mainTabModel = mainTabModel;
		mainTabModel.addPropertyChangeListener("selectedTab", new PropertyChangeListener(){
			public void propertyChange(PropertyChangeEvent evt) {
				updateTopNewDataTimer.run();
			}
		});
	}

	public void registerMyEntriesModel(final MyEntriesModel myEntriesModel) {
		this.myEntriesModel = myEntriesModel;
		myEntriesModel.addPropertyChangeListener("attached", new PropertyChangeListener(){
			public void propertyChange(PropertyChangeEvent evt) {
				doGetMyEntries();
			}
		});
		myEntriesModel.addPropertyChangeListener("offset", new PropertyChangeListener(){
			public void propertyChange(PropertyChangeEvent evt) {
				doGetMyEntries();
			}
		});
		myEntriesModel.addPropertyChangeListener("limit", new PropertyChangeListener(){
			public void propertyChange(PropertyChangeEvent evt) {
				doGetMyEntries();
			}
		});
	}

	public void registerSearchModel(final SearchModel searchModel) {
		this.searchModel = searchModel;
	}

	public void registerTopNewModel(TopNewModel topNewModel) {
		this.topNewModel = topNewModel;
	}

	public void registerUserStatsModel(UserStatsModel userStatsModel) {
		this.userStatsModel = userStatsModel;
	}

	private void doGetMyEntries() {
		if(!myEntriesModel.isAttached())
			return;
		DataRequest datarequestMyEntries = DataModel.DATAREQUEST_MY_ENTRIES;
		datarequestMyEntries.setParameter("userid", authenticationModel.getUser().getId());
		datarequestMyEntries.setParameter("offset", myEntriesModel.getOffset()+"");
		datarequestMyEntries.setParameter("limit", myEntriesModel.getLimit()+"");
		dataModel.addDataRequest(datarequestMyEntries);
	}

	private void updateDataModel() {
	}

	private void updateDataRequests() {
		final List<DataRequest> dataRequests = dataModel.getDataRequests();
		if(dataRequests == null || dataRequests.isEmpty())
			return;
		
		if(dataRequests.contains(DataModel.DATAREQUEST_SEARCH))
			eventManager.fireEvent(new MessageEvent("buscando...", MessageEvent.TYPE_ACTIVITY));
		if(dataRequests.contains(DataModel.DATAREQUEST_MY_ENTRIES))
			eventManager.fireEvent(new MessageEvent("buscando registros...", MessageEvent.TYPE_ACTIVITY));
	
		dataModel.setDataRequests(new ArrayList<DataRequest>());
		DataService.Util.getInstance().getData(dataRequests, new AsyncCallback<DataResult>(){
			public void onFailure(Throwable caught) {
				caught.printStackTrace();
			}
			public void onSuccess(DataResult result) {
				for (DataRequest dataRequest : dataRequests) {
					if(dataRequest.isCanceled())
						continue;
					if(topNewModel != null && dataRequest.equals(DataModel.DATAREQUEST_POLL)){
						String name = DataModel.DATAREQUEST_POLL.getParameters("type");
						topNewModel.setItemsTop(name, result.getItemsTop());
						topNewModel.setItemsNew(name, result.getItemsNew());
						UserStats userStats = result.getUserStats();
						userStatsModel.setCountOnline(userStats.getCountOnline());
						userStatsModel.setCountLoggedIn(userStats.getCountLoggedIn());
						userStatsModel.setLastLoggedIn(userStats.getLastLoggedIn());
					}
					if(myEntriesModel != null && dataRequest.equals(DataModel.DATAREQUEST_MY_ENTRIES)){
						SearchResult<ModelObject> myEntries = result.getMyEntries();
						int count = myEntries.getCount();
						myEntriesModel.setCount(count);
						eventManager.fireEvent(new MessageEvent(count > 0 ? null : 
							"no se encontraron ningunos registros suyos", MessageEvent.TYPE_INFO));
						myEntriesModel.setModelObjects(myEntries.getObjects());
					}
					if(searchModel != null && dataRequest.equals(DataModel.DATAREQUEST_SEARCH)){
						SearchResult<ModelObject> searchResults = result.getSearchResults();
						if(searchResults.getCount() == 0)
							EventManagerImpl.getInstance().fireEvent(new SearchInfoEvent(result.getSearchInfo()));
						EventManagerImpl.getInstance().fireEvent(new SearchResultEvent(searchResults));
					}
				}
			}
		});
	}

}
